//
//  TTRTCServer.swift
//  TTCOM-iOS
//
//  Created by Toj on 2/1/21.
//

import Foundation

open class TTRTCServer: NSObject {
    
    private override init() { }
    public static let shared = TTRTCServer()
    
    private var rtc: TTRTC?
    private var rtcMode: TTRTCMode = .passivity
    
    private var rtc_Service: TTRTCProtocol?
    private var rtcService: TTRTCProtocol?
    
    public func setupService(rtc: TTRTC, rtcMode: TTRTCMode = .passivity) {
        guard self.rtc?.type != rtc.type else { return }
        self.rtc = rtc
        self.rtcMode = rtcMode
        
        videoCanvases.removeAll()
        var service: TTRTCService?
        var aRtcService: TTRTCProtocol?
        
        if rtc.type == .agora {
            guard let aRTC = rtc as? TTRTCAgora else { return }
            let aService = TTRTCAgoraService.shared
            // 初始化参数
            aService.setupAgoraParameter(appID: aRTC.appID, token: aRTC.channel_key)
            service = aService
            aRtcService = aService
        }
        
        if rtc.type == .zego {
            guard let aRTC = rtc as? TTRTCZego else { return }
            let aService = TTRTCZegoService.shared
            // 初始化参数
            aService.setupZegoParameter(appID: aRTC.appID, signString: aRTC.signString)
            service = aService
            aRtcService = aService
        }
        
        rtc_Service = rtcService
        rtcService = aRtcService

        if let bService = service {
            setupMakeService(service: bService)
        }
    }
    
    public var logFilePath: String = ""
    
    // 本地即将进入回调
    public var localWillJoinBlock: TTRTCLocalWillJoin?
    // 本地已经进入回调
    public var localDidJoinedBlock: TTRTCLocalDidJoined?
    // 本地重新进入回调
    public var localReJoinedBlock: TTRTCLocalDidReJoined?
    // 本地第一帧回调
    public var localFirstVideoDecodedBlock: TTRTCFirstVideoDecoded?
    
    // 远端进入回调
    public var remoteDidJoinedBlock: TTRTCRemoteDidJoined?
    // 远端第一帧回调
    public var remoteFirstVideoDecodedBlock: TTRTCFirstVideoDecoded?
    
    // 视频(关闭/打开)回调
    public var didVideoMutedBlock: TTRTCDidStreamMuted?
    // 音频(关闭/打开)回调
    public var didAudioMutedBlock: TTRTCDidStreamMuted?
    // 视频质量回调
    public var videoNetworkQualityBlock: TTRTCVideoNetworkQuality?
    // 音频质量回调
    public var audioVolumeIndicationBlock: TTRTCAudioVolumeIndication?
    
    // 有用户离开
    public var remoteDidOfflineBlock: TTRTCDidOffline?
    
    // Token已经失效
    public var tokenDidTimeoutBlock: TTRTCDidTimeout?
    // 发生错误回调
    public var didOccurErrorBlock: TTRTCDidOccurError?
    // 接收对方数据流消息错误的回调
    public var didOccurStreamMessageErrorBlock: TTRTCDidOccurStreamMessageError?
    
    private var switchRTCBlock: ((_ code: Int)->Void)?
    
    private var videoCanvases: [String: VideoCanvasCache] = [:]
}

extension TTRTCServer {
    public func joinLiveRoom(userID: TTUInt, roomID: String, local view: TTView) {
        let _ = setupVideoCanvasCach(uid: userID, view: view)
        rtcService?.joinLiveRoom(userID: userID, roomID: roomID, local: view)
    }
    
    public func initiativePullRemoteVideo(uid: TTUInt, remote view: TTView) {
        rtcService?.pullRemoteVideo(uid: uid, remote: view)
    }
    
    public func initiativePullRemoteVideo(uid: TTUInt) {
        let view = videoCanvasView(uid: uid)
        rtcService?.pullRemoteVideo(uid: uid, remote: view)
    }
    
    public func switchStream(uid old: TTUInt, uid new: TTUInt) {
        let videoCanvas = videoCanvases[old.to_string]
        guard let remoteView = videoCanvas?.view else { return }
        
        rtcService?.switchStream(uid: new, remote: remoteView)
        rtcService?.pullRemoteVideoStream(uid: old, muted: true)
        rtcService?.pullRemoteVideoStream(uid: new, muted: false)
        videoCanvases[new.to_string] = videoCanvas
        videoCanvases.removeValue(forKey: old.to_string)
    }
    
    public func quitLiveRoom(complete quitBlock: TTRTCQuitLiveRoom?) {
        rtcService?.quitLiveRoom(complete: { [weak self] in
            self?.destroyCach()
            quitBlock?()
        })
    }
    
    public func switchRemoteVideoStream(uid: TTUInt, type: TTVideoStreamType) {
        rtcService?.switchRemoteVideoStream(uid: uid, type: type)
    }
    
    public func localVideo(enabled: Bool) {
        rtcService?.localVideo(enabled: enabled)
    }
    
    public func pushLocalVideoStream(muted: Bool) {
        rtcService?.pushLocalVideoStream(muted: muted)
    }
    
    public func pushLocalAudioStream(muted: Bool) {
        rtcService?.pushLocalAudioStream(muted: muted)
    }
    
    public func pullRemoteVideoStream(uid: TTUInt, muted: Bool) {
        rtcService?.pullRemoteVideoStream(uid: uid, muted: muted)
    }
    
    public func pullRemoteAudioStream(uid: TTUInt, muted: Bool) {
        rtcService?.pullRemoteAudioStream(uid: uid, muted: muted)
    }
    
    public func pullRemoteVideoStreamAll(muted: Bool) {
        rtcService?.pullRemoteVideoStreamAll(muted: muted)
    }
    
    public func pullRemoteAudioStreamAll(muted: Bool) {
        rtcService?.pullRemoteAudioStreamAll(muted: muted)
    }
    
    public func beautyEffect(enable: Bool) {
        rtcService?.beautyEffect(enable: enable)
    }
    
    #if canImport(Cocoa)
    public func sharedFullScreen() {
        let result = rtcService?.sharedFullScreen() ?? -1
        print(result)
    }
    
    public func sharedScreenWindowId(windowId: TTUInt, size: CGSize) {
        let result = rtcService?.sharedScreenWindowId(windowId: windowId, size: size) ?? -1
        print(result)
    }
    
    public func sharedScreenDisplayId(displayId: TTUInt, size: CGSize) {
        let result = rtcService?.sharedScreenDisplayId(displayId: displayId, size: size) ?? -1
        print(result)
    }
    
    public func stopScreenCapture() {
        let result = rtcService?.stopScreenCapture() ?? -1
        print(result)
    }
    #endif
    
    public func switchRTC(rtc new: TTRTC, completed: @escaping ((_ code: Int)->Void)) {
        switchRTCBlock = completed
        setupService(rtc: new)
    }
}

extension TTRTCServer {
    
    private func quitBeforeRoom() {
        rtc_Service?.quitLiveRoom(complete:nil)
        rtc_Service = nil
        switchRTCBlock?(0)
        switchRTCBlock = nil
    }
    
    private func rollbackRoom() {
        rtcService?.quitLiveRoom(complete:nil)
        rtcService = rtc_Service
        switchRTCBlock = nil
    }
    
    private func destroyCach() {
        videoCanvases.forEach { (uid, videoCanvas) in
            videoCanvas.view?.removeFromSuperview()
        }
        videoCanvases.removeAll()
        rtc = nil
        rtc_Service = nil
        rtcService = nil
    }
    
    private func setupVideoCanvasCach(uid: TTUInt, view: TTView) -> VideoCanvasCache {
        var videoCanvas = videoCanvases[uid.to_string]
        if videoCanvas == nil {
            videoCanvas = VideoCanvasCache()
            videoCanvas?.view = view
            videoCanvases[uid.to_string] = videoCanvas
        }
        
        return videoCanvas!
    }
    
    // 创建View
    private func videoCanvasView(uid: TTUInt) -> TTView {
        let view = TTView()
        _ = setupVideoCanvasCach(uid: uid, view: view)
        return view
    }
}


extension TTRTCServer {
    private func setupMakeService(service: TTRTCService) {
        
        setupMakeServiceLocal(service: service)
        setupMakeServiceRemote(service: service)
        setupMakeServiceOther(service: service)
        setupMakeServiceError(service: service)
    }
}

extension TTRTCServer {
    private func setupMakeServiceLocal(service: TTRTCService) {
        
        // service.localWillJoinBlock = { (uid) in }
        service.localWillJoinBlock = self.localWillJoinBlock
        
        service.localDidJoinedBlock = { [weak self] (uid) in
            self?.quitBeforeRoom()
            self?.localDidJoinedBlock?(uid)
        }
        
        // service.localReJoinedBlock = { (uid) in }
        service.localReJoinedBlock = self.localReJoinedBlock
        
        // service.localFirstVideoDecodedBlock = { (uid) in }
        service.localFirstVideoDecodedBlock = self.localFirstVideoDecodedBlock
    }
    
    private func setupMakeServiceRemote(service: TTRTCService) {
        
        service.runTimeRemoteViewBlock = {
            [weak self] (uid) -> TTView? in
            // 被动模式才能创建View, 其他模式不创建
            guard self?.rtcMode == .passivity else { return nil }
            let view = self?.videoCanvasView(uid: uid)
            return view
        }
        
        // service.remoteDidJoinedBlock = { (uid) in }
        service.remoteDidJoinedBlock = self.remoteDidJoinedBlock
        
        // service.remoteFirstVideoDecodedBlock = { (uid) in }
        service.remoteFirstVideoDecodedBlock = self.remoteFirstVideoDecodedBlock
        
        // service.remoteDidOfflineBlock = { (uid) in }
        service.remoteDidOfflineBlock = self.remoteDidOfflineBlock
    }
    
    private func setupMakeServiceOther(service: TTRTCService) {
        
        // service.didVideoMutedBlock = { (muted, uid) in }
        service.didVideoMutedBlock = self.didVideoMutedBlock
        
        // service.didAudioMutedBlock = { (muted, uid) in }
        service.didAudioMutedBlock = self.didAudioMutedBlock
        
        // service.videoNetworkQualityBlock = { (uid, level) in }
        service.videoNetworkQualityBlock = self.videoNetworkQualityBlock

        // service.audioVolumeIndicationBlock = { (uid, volume) in }
        service.audioVolumeIndicationBlock = self.audioVolumeIndicationBlock
    }
    
    private func setupMakeServiceError(service: TTRTCService) {
        
        // service.tokenDidTimeoutBlock = { }
        service.tokenDidTimeoutBlock = self.tokenDidTimeoutBlock
        
        service.didOccurErrorBlock = { [weak self] (code) in
            if self?.rtc_Service != nil {
                self?.switchRTCBlock?(code)
                self?.rollbackRoom()
                return
            }
            self?.didOccurErrorBlock?(code)
        }
        
        // service.didOccurStreamMessageErrorBlock = { (uid, code) in }
        service.didOccurStreamMessageErrorBlock = { [weak self] (uid, code) in
            self?.didOccurStreamMessageErrorBlock?(uid, code)
        }
    }
}
